// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/11/16
// TODO:
//

#include "HttpConn.h"

#include <fcntl.h>
#include <unistd.h>

bool HttpConn::is_et_model_ = false;
const char *HttpConn::src_dir_ = nullptr;
std::atomic<int> HttpConn::user_count_(0);

HttpConn::HttpConn() {
  fd_ = -1;
  addr_ = {0};
  is_closed_ = true;
}

int HttpConn::getFd() const { return fd_; }

const sockaddr_in &HttpConn::getAddr() const { return addr_; }

const char *HttpConn::get_ip() const { return inet_ntoa(addr_.sin_addr); }

int HttpConn::get_port() const { return addr_.sin_port; }

void HttpConn::init(int fd, const sockaddr_in &addr) {
  assert(fd > 0);
  addr_ = addr;
  user_count_++;
  write_buffer_.RetrieveAll();
  read_buffer_.RetrieveAll();
  is_closed_ = false;

  LOG_INFO("Client [%d] (%s:%d) connected in, now user count is: %d\n", fd_,
           get_ip(), get_port());
}

ssize_t HttpConn::read(int *save_errno) {
  ssize_t read_len = -1;

  do {
    read_len = read_buffer_.ReadFd(fd_, save_errno);
    if (read_len <= 0) {
      break;
    }
  } while (is_et_model_);
  return read_len;
}

int HttpConn::to_write_bytes() { return iov_[0].iov_len + iov_[1].iov_len; }

ssize_t HttpConn::write(int *save_errno) {
  ssize_t write_len = -1;

  do {
    write_len = writev(fd_, iov_, iov_cnt_);
    if (write_len <= 0) {
      *save_errno == errno;
      break;
    }

    if (iov_[0].iov_len + iov_[1].iov_len == 0) {
      break;
    } else if (static_cast<size_t>(write_len) > iov_[0].iov_len) {
      iov_[1].iov_base =
          (uint8_t *)iov_[1].iov_base + (write_len - iov_[0].iov_len);
      iov_[1].iov_len -= (write_len - iov_[0].iov_len);
      if (iov_[0].iov_len) {
        write_buffer_.RetrieveAll();
        iov_[0].iov_len = 0;
      }
    } else {
      iov_[0].iov_base = (uint8_t *)iov_[0].iov_base + write_len;
      iov_[0].iov_len -= write_len;
      write_buffer_.Retrieve(write_len);
    }
  } while (is_et_model_ || to_write_bytes() > 10240);
  return write_len;
}

HttpConn::~HttpConn() { close(); }

bool HttpConn::is_keep_alive() const { return request_.is_keep_alive(); }

void HttpConn::close() {
  response_.unmap_file();
  if (is_closed_ == false) {
    is_closed_ = true;
    user_count_--;
    ::close(fd_);
    LOG_INFO("Client [%d](%s:%d) quit, now user count: %d.\n", fd_, get_ip(),
             get_port(), (int)user_count_);
  }
}

bool HttpConn::process() {
  request_.init();
  if (read_buffer_.ReadableBytes() <= 0) {
    return false;
  } else if (request_.parse(read_buffer_)) {
    //    LOG_DEBUG("%s")
    response_.init(src_dir_, request_.path(), request_.is_keep_alive(), 200);
  } else {
    response_.init(src_dir_, request_.path(), false, 400);
  }

  response_.make_response(write_buffer_);

  iov_[0].iov_base = const_cast<char *>(write_buffer_.Peek());
  iov_[0].iov_len =write_buffer_.ReadableBytes();
  iov_cnt_ = 1;

  if (response_.file_len() > 0 && response_.getMmFile()) {
    iov_[1].iov_base = response_.getMmFile();
    iov_[1].iov_len = response_.file_len();
    iov_cnt_ = 2;
  }

  return true;
}
